#include <gtest/gtest.h>          // Google Test 框架头文件
#include "nlohmann/json.hpp"      // JSON 处理库

#include <string>                 // 字符串处理
#include <vector>                 // 动态数组
#include <fstream>                // 文件流操作 (ifstream, ofstream)
#include <iostream>               // 输入输出流 (用于打印错误信息)
#include <filesystem>             // 文件系统操作 (需要 C++17)
#include <mutex>                  // 互斥锁 (虽然测试中不直接模拟并发，但函数可能需要)
#include <algorithm>              // 算法 (如 std::sort)
#include <limits>                 // 用于获取数值类型的极限值
#include <regex>                  // 正则表达式 (用于时间戳验证)
#include <chrono>                 // 时间处理
#include <iomanip>                // I/O 格式化 (如 std::setw, std::setfill)
#include <sstream>                // 字符串流

// 使用 nlohmann::json 简化 JSON 操作
using json = nlohmann::json;
// 使用 std::filesystem 简化文件系统操作
namespace fs = std::filesystem;

// --- 配置 (与 main.cpp 保持一致或用于测试) ---
// 为测试使用一个单独的文件名，避免覆盖实际数据
const std::string TEST_LEADERBOARD_FILE = "test_leaderboard.json";
const size_t MAX_LEADERBOARD_SIZE = 100; // 与 main.cpp 中的限制保持一致

// --- 辅助函数 (原型声明或直接复制实现) ---
// 最佳实践是将这些函数放在共享的头文件 (.h) 中，并链接其实现 (.cpp 或 .o 文件)。
// 为简化示例，这里假设这些函数可用，或者在下面重新实现它们。

// 函数前向声明 (假设实现在别处或下面提供)
json loadScoresFromFile(const std::string& filename);
bool saveScoresToFile(const std::string& filename, const json& scores_data);
std::string getCurrentTimestampISO8601(); // 假设这个函数存在且行为正确

// --- 测试夹具 (Test Fixture) ---
// 测试夹具类，用于共享设置和清理逻辑
class LeaderboardApiTest : public ::testing::Test {
protected:
    // 可选：整个测试套件运行前的设置 (如果需要)
    // static void SetUpTestSuite() {}

    // 可选：整个测试套件运行后的清理 (如果需要)
    // static void TearDownTestSuite() {}

    // 每个 TEST_F 测试用例运行前的设置
    void SetUp() override {
        // 确保每个测试开始前都有一个干净的状态
        fs::remove(TEST_LEADERBOARD_FILE); // 如果测试文件存在，则删除
        fs::remove(TEST_LEADERBOARD_FILE + ".bak"); // 清理可能的备份文件

        // 创建一个空的排行榜文件作为大多数测试的初始状态
        json empty_array = json::array();
        saveTestScores(empty_array); // 使用下面的 saveTestScores 保存空数组
    }

    // 每个 TEST_F 测试用例运行后的清理
    void TearDown() override {
        // 清理测试生成的文件
        fs::remove(TEST_LEADERBOARD_FILE);
        fs::remove(TEST_LEADERBOARD_FILE + ".bak"); // 以防万一
    }

    // --- 核心逻辑的重新实现或访问 ---
    // 在测试夹具中重新实现加载/保存逻辑，强制使用测试文件名

    // 从测试文件加载分数
    json loadTestScores() {
        json scores_data = json::array(); // 默认空数组
        std::ifstream file(TEST_LEADERBOARD_FILE); // 打开测试文件
        if (file.is_open()) {
            try {
                file >> scores_data; // 尝试从文件流解析 JSON
                // 确保解析结果是 JSON 数组
                if (!scores_data.is_array()) {
                    std::cerr << "测试警告: " << TEST_LEADERBOARD_FILE << " 内容不是有效的 JSON 数组，重置为空数组。" << std::endl;
                    scores_data = json::array();
                }
            } catch (json::parse_error& e) {
                // 文件内容为空或无效 JSON 时会抛出异常
                // 通过 peek() 检查是否真的到达文件末尾（空文件）
                if (file.peek() != std::ifstream::traits_type::eof()) {
                    // 文件非空但解析失败
                    std::cerr << "测试加载错误: 解析 " << TEST_LEADERBOARD_FILE << " 失败: " << e.what() << std::endl;
                }
                // 对于空文件或解析错误，都返回空数组
                scores_data = json::array();
            }
            file.close(); // 关闭文件
        }
        // 如果文件打不开，也返回空数组
        return scores_data;
    }

    // 将分数保存到测试文件
    bool saveTestScores(const json& scores_data) {
        // 确保要保存的是数组
        if (!scores_data.is_array()) {
             std::cerr << "测试保存错误: 尝试保存非数组数据到 " << TEST_LEADERBOARD_FILE << std::endl;
             return false;
        }
        // 以截断模式打开文件（清空原有内容）
        std::ofstream file(TEST_LEADERBOARD_FILE, std::ios::trunc);
        if (file.is_open()) {
            try {
                // 使用 setw(4) 输出带缩进的美化 JSON
                file << std::setw(4) << scores_data << std::endl;
                file.close(); // 关闭文件
                // 检查写入操作是否成功（流状态是否良好）
                return file.good();
            } catch (const std::exception& e) {
                // 捕获可能的序列化或写入异常
                std::cerr << "测试保存错误: 写入 " << TEST_LEADERBOARD_FILE << " 时发生异常: " << e.what() << std::endl;
                file.close(); // 尝试关闭文件
                return false;
            }
        } else {
             std::cerr << "测试保存错误: 无法打开 " << TEST_LEADERBOARD_FILE << " 进行写入。" << std::endl;
             return false;
        }
    }

    // 模拟 POST /api/scores 的核心处理逻辑，用于测试数据操作
    // 成功时返回新创建的记录，失败时返回空 JSON 对象 {}
    json simulatePostScore(const json& score_input) {
        // --- 输入验证 (简化版，模拟 main.cpp 中的检查) ---
        bool valid = true;
        if (!score_input.contains("playerName") || !score_input["playerName"].is_string()) {
            valid = false;
        } else {
            std::string name = score_input["playerName"].get<std::string>();
            if (name.empty() || std::all_of(name.begin(), name.end(), ::isspace) || name.length() > 50) {
                valid = false;
            }
        }
        if (!score_input.contains("score") || !score_input["score"].is_number_integer() || score_input["score"].get<int>() < 0) {
            valid = false;
        }

        if (!valid) {
            // std::cout << "模拟 POST 验证失败: " << score_input.dump() << std::endl; // 调试信息
            return json::object(); // 返回空对象表示验证失败
        }

        // --- 处理分数 ---
        json scores = loadTestScores(); // 加载当前测试数据
        if (!scores.is_array()) {
             std::cerr << "模拟 POST 错误: 加载测试数据失败。" << std::endl;
             return json::object(); // 加载失败
        }

        // 创建新记录
        json new_score_record;
        new_score_record["playerName"] = score_input["playerName"];
        new_score_record["score"] = score_input["score"];
        new_score_record["timestamp"] = getCurrentTimestampISO8601(); // 使用实际的时间戳函数

        scores.push_back(new_score_record); // 添加到内存中的列表

        // 排序 (降序)
        std::sort(scores.begin(), scores.end(), [](const json& a, const json& b) {
            // 安全地获取分数，处理可能的类型错误或字段缺失
            int score_a = 0, score_b = 0;
            if (a.is_object() && a.contains("score") && a["score"].is_number()) score_a = a["score"];
            if (b.is_object() && b.contains("score") && b["score"].is_number()) score_b = b["score"];
            return score_a > score_b; // 降序排序
        });

        // 限制排行榜大小
        if (scores.size() > MAX_LEADERBOARD_SIZE) {
            // 从末尾（分数最低的）开始删除多余的记录
            scores.erase(scores.begin() + MAX_LEADERBOARD_SIZE, scores.end());
        }

        // 保存更新后的数据到测试文件
        if (saveTestScores(scores)) {
            return new_score_record; // 成功，返回新创建的记录
        } else {
             std::cerr << "模拟 POST 错误: 保存测试数据失败。" << std::endl;
             return json::object(); // 保存失败
        }
    }

    // 模拟 GET /api/scores 的核心处理逻辑
    json simulateGetScores(int limit = 10) {
        json scores = loadTestScores(); // 加载测试数据
        if (!scores.is_array()) {
             std::cerr << "模拟 GET 错误: 加载测试数据失败。" << std::endl;
             return json::array(); // 加载失败返回空数组
        }

        // 复制到 vector 中进行排序（更安全，避免迭代器失效）
        std::vector<json> sorted_scores_vec;
        for(const auto& score_item : scores) {
            // 确保是包含有效分数的对象
            if (score_item.is_object() && score_item.contains("score") && score_item["score"].is_number()) {
                sorted_scores_vec.push_back(score_item);
            } else {
                 std::cerr << "模拟 GET 警告: 跳过无效的排行榜记录: " << score_item.dump() << std::endl;
            }
        }

        // 排序 (降序)
        std::sort(sorted_scores_vec.begin(), sorted_scores_vec.end(), [](const json& a, const json& b) {
            // 使用 value() 提供默认值，更安全
            int score_a = a.value("score", std::numeric_limits<int>::min());
            int score_b = b.value("score", std::numeric_limits<int>::min());
            return score_a > score_b; // 降序
        });

        // 应用 limit (如果 limit > 0)
        if (limit > 0 && sorted_scores_vec.size() > static_cast<size_t>(limit)) {
            sorted_scores_vec.resize(limit); // 截断 vector
        }

        // 将处理后的 vector 转换回 JSON 数组并返回
        return json(sorted_scores_vec);
    }

    // 辅助函数：检查时间戳格式是否有效
    static bool isValidTimestamp(const std::string& ts) {
        // 基本的 ISO 8601 UTC 格式检查 (YYYY-MM-DDTHH:MM:SSZ)
        static const std::regex iso8601_regex("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z");
        return std::regex_match(ts, iso8601_regex);
    }
};

// --- 测试用例 ---
// 使用 TEST_F 宏来定义属于 LeaderboardApiTest 夹具的测试用例

// 测试：获取空的排行榜
TEST_F(LeaderboardApiTest, GetEmptyLeaderboard) {
    json result = simulateGetScores();
    // 断言结果是数组类型
    ASSERT_TRUE(result.is_array()) << "结果应为 JSON 数组";
    // 断言数组为空
    ASSERT_TRUE(result.empty()) << "初始排行榜应为空";
}

// 测试：提交一个有效的得分记录
TEST_F(LeaderboardApiTest, PostOneValidScore) {
    json input = {{"playerName", "玩家一"}, {"score", 15000}};
    json created_record = simulatePostScore(input);

    // 断言模拟提交操作成功（返回非空对象）
    ASSERT_FALSE(created_record.empty()) << "模拟提交应成功并返回记录";
    // 检查返回记录的内容
    ASSERT_EQ(created_record["playerName"], "玩家一");
    ASSERT_EQ(created_record["score"], 15000);
    ASSERT_TRUE(created_record.contains("timestamp")) << "记录应包含时间戳";
    // 检查时间戳格式
    ASSERT_TRUE(isValidTimestamp(created_record["timestamp"].get<std::string>()))
        << "时间戳 " << created_record["timestamp"] << " 格式无效";

    // 验证文件内容是否正确更新
    json scores_in_file = loadTestScores();
    ASSERT_EQ(scores_in_file.size(), 1) << "文件中应有一条记录";
    ASSERT_EQ(scores_in_file[0]["playerName"], "玩家一");
    ASSERT_EQ(scores_in_file[0]["score"], 15000);
    ASSERT_TRUE(scores_in_file[0].contains("timestamp"));
}

// 测试：提交一个得分后获取排行榜
TEST_F(LeaderboardApiTest, GetLeaderboardAfterPost) {
    json input = {{"playerName", "Alice"}, {"score", 5000}};
    simulatePostScore(input); // 先提交一个分数

    json result = simulateGetScores(); // 获取排行榜
    ASSERT_EQ(result.size(), 1) << "获取时应返回一条记录";
    ASSERT_EQ(result[0]["playerName"], "Alice");
    ASSERT_EQ(result[0]["score"], 5000);
}

// 测试：提交多个得分并检查排序
TEST_F(LeaderboardApiTest, PostMultipleScoresCheckOrder) {
    // 以不同顺序提交分数
    simulatePostScore({{"playerName", "Charlie"}, {"score", 10000}});
    simulatePostScore({{"playerName", "Alice"}, {"score", 15000}}); // 最高分
    simulatePostScore({{"playerName", "Bob"}, {"score", 12000}});

    json result = simulateGetScores(); // 获取（默认 limit 10）
    ASSERT_EQ(result.size(), 3) << "应返回三条记录";
    // 检查排序是否按分数降序
    EXPECT_EQ(result[0]["playerName"], "Alice") << "第一名应是 Alice (15000)";
    EXPECT_EQ(result[0]["score"], 15000);
    EXPECT_EQ(result[1]["playerName"], "Bob") << "第二名应是 Bob (12000)";
    EXPECT_EQ(result[1]["score"], 12000);
    EXPECT_EQ(result[2]["playerName"], "Charlie") << "第三名应是 Charlie (10000)";
    EXPECT_EQ(result[2]["score"], 10000);
}

// 测试：使用 limit 参数获取排行榜
TEST_F(LeaderboardApiTest, GetLeaderboardWithLimit) {
    simulatePostScore({{"playerName", "D"}, {"score", 5000}});
    simulatePostScore({{"playerName", "A"}, {"score", 25000}}); // 最高
    simulatePostScore({{"playerName", "C"}, {"score", 15000}});
    simulatePostScore({{"playerName", "B"}, {"score", 20000}}); // 第二

    json result = simulateGetScores(2); // 获取前 2 名
    ASSERT_EQ(result.size(), 2) << "使用 limit=2 时应返回两条记录";
    // 检查返回的是否是分数最高的两条
    EXPECT_EQ(result[0]["playerName"], "A") << "第一条应是 A (25000)";
    EXPECT_EQ(result[1]["playerName"], "B") << "第二条应是 B (20000)";
}

// 测试：提交缺少 playerName 的数据
TEST_F(LeaderboardApiTest, PostScoreMissingPlayerName) {
    json input = {{"score", 100}};
    json result = simulatePostScore(input);
    // 断言模拟提交失败（返回空对象）
    ASSERT_TRUE(result.empty()) << "缺少 playerName 时提交应失败";
    // 验证文件内容未被修改（仍为空）
    json scores_in_file = loadTestScores();
    ASSERT_TRUE(scores_in_file.empty()) << "文件内容应保持不变";
}

// 测试：提交缺少 score 的数据
TEST_F(LeaderboardApiTest, PostScoreMissingScore) {
    json input = {{"playerName", "NoScore"}};
    json result = simulatePostScore(input);
    ASSERT_TRUE(result.empty()) << "缺少 score 时提交应失败";
    json scores_in_file = loadTestScores();
    ASSERT_TRUE(scores_in_file.empty());
}

// 测试：提交 score 类型无效（字符串）的数据
TEST_F(LeaderboardApiTest, PostScoreInvalidScoreType) {
    json input = {{"playerName", "BadScore"}, {"score", "not a number"}};
    json result = simulatePostScore(input);
    ASSERT_TRUE(result.empty()) << "score 为非整数时提交应失败";
    json scores_in_file = loadTestScores();
    ASSERT_TRUE(scores_in_file.empty());
}

// 测试：提交负数 score 的数据
TEST_F(LeaderboardApiTest, PostScoreNegativeScore) {
    json input = {{"playerName", "Negative"}, {"score", -100}};
    json result = simulatePostScore(input);
    ASSERT_TRUE(result.empty()) << "score 为负数时提交应失败";
    json scores_in_file = loadTestScores();
    ASSERT_TRUE(scores_in_file.empty());
}

// 测试：提交空的 playerName
TEST_F(LeaderboardApiTest, PostScoreEmptyPlayerName) {
    json input = {{"playerName", ""}, {"score", 100}};
    json result = simulatePostScore(input);
    ASSERT_TRUE(result.empty()) << "playerName 为空字符串时提交应失败";
    json scores_in_file = loadTestScores();
    ASSERT_TRUE(scores_in_file.empty());
}

// 测试：提交过长的 playerName
TEST_F(LeaderboardApiTest, PostScoreLongPlayerName) {
    std::string long_name(51, 'a'); // 创建一个 51 个 'a' 的字符串
    json input = {{"playerName", long_name}, {"score", 100}};
    json result = simulatePostScore(input);
    ASSERT_TRUE(result.empty()) << "playerName 过长时提交应失败";
    json scores_in_file = loadTestScores();
    ASSERT_TRUE(scores_in_file.empty());
}

// --- 可以添加更多测试用例，例如测试文件加载/保存失败的情况 ---

// 测试：加载一个格式错误的 JSON 文件
TEST_F(LeaderboardApiTest, LoadMalformedFile) {
    // 手动创建一个无效的 JSON 文件
    {
        std::ofstream bad_file(TEST_LEADERBOARD_FILE, std::ios::trunc);
        bad_file << "{ not json ]"; // 写入无效内容
        bad_file.close();
    }
    json result = loadTestScores();
    // 断言加载函数在这种情况下返回空数组
    ASSERT_TRUE(result.is_array()) << "加载无效文件应返回数组";
    ASSERT_TRUE(result.empty()) << "加载无效文件应返回空数组";
}